// 防止头文件被多次包含的宏守卫
#pragma once

// === 标准库头文件 ===

// 包含标准输入输出流库，用于 std::cout, std::endl 等
#include <iostream>
// 包含字符串类库，用于 std::string, std::to_string
#include <string>
// 包含功能对象库，用于 std::function (在 task_t 中使用)
#include <functional>
// 包含基本系统数据类型，如 pid_t (在 getpid 中使用，尽管 getpid 也需要
// unistd.h)
#include <sys/types.h> /* See NOTES */ // 通常包含套接字和文件系统相关的基本类型定义
// 包含套接字接口定义，如 socket, bind, listen, accept, shutdown, close 等
#include <sys/socket.h>
// 包含互联网地址族定义，如 sockaddr_in, AF_INET, SOCK_STREAM, IPPROTO_TCP,
// INADDR_ANY, htons 等
#include <netinet/in.h>
// 包含 IP 地址操作函数，如 inet_pton, inet_ntop (以及旧的 inet_addr, inet_ntoa)
#include <arpa/inet.h>
// 包含 POSIX 标准库，提供系统调用接口，如 fork, exit, read, write, close,
// sleep, getpid 等
#include <unistd.h>
// 包含 C 风格字符串处理函数，如 memset, strlen 等
#include <cstring>
// 包含 POSIX 线程库，用于 pthread_create, pthread_detach, pthread_join,
// pthread_self 等
#include <pthread.h>
// 再次包含基本系统数据类型 (有时为了兼容性或特定类型会重复包含)
// #include <sys/types.h> // 这行是重复的，可以考虑移除，上面的已经包含了
// 包含进程等待函数，如 wait, waitpid 等 (用于多进程模式)
#include <sys/wait.h>



// 包含网络地址封装类 InetAddr 的头文件
// 假设 InetAddr 封装了 sockaddr_in 或 sockaddr_in6，并提供了获取 IP
// 字符串和端口号的方法
#include "InetAddr.hpp"
// 包含自定义日志系统 Log 的头文件 (提供 LOG 宏等)
#include "Log.hpp"
// 包含自定义线程池 ThreadPool 的头文件
// 假设 ThreadPool 是一个模板类，用于管理工作线程执行任务
#include "ThreadPool.hpp"

// 定义错误码枚举，方便程序在不同错误时返回特定的退出码
enum {
  SOCKET_ERROR = 1, // 表示 Socket 创建失败的错误码
  BIND_ERROR,       // 表示 Socket 绑定地址失败的错误码
  LISTEN_ERROR,     // 表示 Socket 监听失败的错误码
  USAGE_ERROR       // 表示程序命令行参数使用错误的错误码
                    // (虽然在此文件中未使用，但可能是项目共用)
};

// 常量定义
const static int defaultsockfd =
    -1;                         // 定义一个默认值表示无效的 Socket 文件描述符
const static int gbacklog = 16; // TCP 监听队列的长度（等待接受的连接队列大小）

// 前向声明 TcpServer 类
// 告诉编译器 TcpServer 是一个类，但具体定义在后面。
// 这允许 ThreadData 类在其定义中使用 TcpServer* 类型的指针。
class TcpServer;

/**
 * @brief ThreadData 类：用于向新线程传递处理客户端连接所需的数据。
 *
 * 当接受一个新的客户端连接并为其创建一个新线程（或进程）来处理时，
 * 这个类用于封装需要传递给该新执行流的参数，如已连接的套接字描述符、
 * 客户端的地址信息以及指向父 TcpServer 实例的指针。
 */
class ThreadData {
public:
  /**
   * @brief ThreadData 类的构造函数。
   * @param fd 已连接的客户端套接字文件描述符。
   * @param addr 客户端的网络地址信息 (封装在 InetAddr 对象中)。
   * @param s 指向创建此 ThreadData 的 TcpServer 实例的指针。
   */
  ThreadData(int fd, InetAddr addr, TcpServer *s)
      : sockfd(fd),       // 初始化客户端套接字文件描述符
        clientaddr(addr), // 初始化客户端地址信息
        self(s)           // 初始化指向 TcpServer 实例的指针
  {
    // 构造函数体为空
  }

public:
  int sockfd;          // 存储已连接的客户端套接字文件描述符
  InetAddr clientaddr; // 存储客户端的网络地址信息
  TcpServer
      *self; // 存储指向父 TcpServer 对象的指针，以便在线程中调用服务器的方法
};

// 定义任务类型别名 task_t，用于线程池中的任务。
// std::function<void()> 表示一个不接受参数且返回 void 的可调用对象
// (如函数指针、lambda、成员函数绑定等)。
// 这是线程池中任务队列期望存储的任务类型。
using task_t = std::function<void()>;

/**
 * @brief TcpServer 类：实现了一个基础的 TCP 服务器。
 *
 * 该类封装了TCP服务器的常见操作：
 * 1. 初始化：创建监听套接字、绑定到指定端口、开始监听连接请求。
 * 2. 运行主循环：接受客户端连接。
 * 3.
 * 并发处理：为每个接受的连接创建新的执行单元（多线程、多进程或线程池任务）来提供服务。
 * 4. 服务逻辑：定义了如何与客户端进行交互（本例中为简单的回显服务）。
 * 5. 资源管理：在析构时关闭监听套接字。
 */
class TcpServer {
public:
  /**
   * @brief TcpServer 类的构造函数。
   * @param port 服务器将要监听的端口号。
   */
  TcpServer(int port)
      : _port(port),                // 初始化服务器监听的端口号
        _listensock(defaultsockfd), // 初始化监听套接字文件描述符为无效值
        _isrunning(false)           // 初始化服务器运行状态为 false (尚未启动)
  {
    // 构造函数体为空
  }

  /**
   * @brief 初始化服务器。
   *
   * 该方法执行服务器启动前必要的设置步骤：
   * 1. 创建一个流式套接字 (TCP) 用于监听。
   * 2. 将该套接字绑定到本地的指定端口和所有可用IP地址。
   * 3. 使该套接字进入监听状态，准备接受客户端的连接请求。
   * 如果任何步骤失败，将记录致命错误日志并退出程序。
   */
  void InitServer() {
    // 1. 创建流式套接字 (TCP Socket)
    // AF_INET: 指定地址族为 IPv4。
    // SOCK_STREAM: 指定套接字类型为流式套接字，提供可靠的、面向连接的通信
    // (TCP)。 0: 指定协议类型。对于 AF_INET 和
    // SOCK_STREAM，通常设为0，系统会自动选择TCP协议。
    // ::socket 前缀表示调用全局命名空间中的 socket 函数。
    _listensock = ::socket(AF_INET, SOCK_STREAM, 0);
    if (_listensock < 0) // 检查 socket() 调用是否成功，小于0表示失败。
    {
      // 套接字创建失败，记录致命错误日志并退出。
      // LOG 宏会自动添加文件名、行号等信息。errno
      // 可以提供更详细的系统错误原因。
      LOG(FATAL, "socket error, errno: %d, errmsg: %s", errno, strerror(errno));
      exit(SOCKET_ERROR); // 使用预定义的错误码退出程序。
    }
    // 套接字创建成功，记录调试日志。
    LOG(DEBUG, "socket create success, sockfd is : %d\n",
        _listensock); // 原有注释中的 \n 保留

    // 2. 绑定 (bind) 套接字到本地地址和端口
    struct sockaddr_in local; // 定义 IPv4 地址结构体，用于存储本地地址信息。
    memset(
        &local, 0,
        sizeof(
            local)); // 将 local
                     // 结构体的内存清零，这是一个好习惯，避免未初始化的垃圾数据。
    local.sin_family = AF_INET;    // 设置地址族为 IPv4。
    local.sin_port = htons(_port); // 设置服务器监听的端口号。htons()
                                   // 将主机字节序的端口号转换为网络字节序。
    local.sin_addr.s_addr =
        INADDR_ANY; // 设置IP地址。INADDR_ANY
                    // 表示服务器将接受来自本机任何网络接口的连接。
                    // 操作系统会自动选择合适的IP地址。INADDR_ANY 通常是
                    // (u_long)0x00000000。

    // 调用 ::bind() 将 _listensock 套接字与 local 地址关联起来。
    // (struct sockaddr *)&local: 将 sockaddr_in* 强制转换为 sockaddr*，因为
    // bind 函数接受通用地址结构指针。
    int n = ::bind(_listensock, (struct sockaddr *)&local, sizeof(local));
    if (n < 0) // 检查 bind() 调用是否成功。
    {
      // 绑定失败，记录致命错误日志并退出。
      LOG(FATAL, "bind error, errno: %d, errmsg: %s", errno, strerror(errno));
      exit(BIND_ERROR);
    }
    // 绑定成功，记录调试日志。
    LOG(DEBUG, "bind success, sockfd is : %d\n",
        _listensock); // 原有注释中的 \n 保留

    // 3. 将套接字设置为监听模式 (listen)
    // TCP是面向连接的，服务器在接受连接之前必须将套接字置于监听状态。
    // _listensock: 要设置为监听状态的套接字。
    // gbacklog:
    // 监听队列的最大长度，即内核为这个监听套接字维护的已完成连接队列和未完成连接队列的总大小的上限（具体解释依赖系统）。
    n = ::listen(_listensock, gbacklog);
    if (n < 0) // 检查 listen() 调用是否成功。
    {
      // 监听失败，记录致命错误日志并退出。
      LOG(FATAL, "listen error, errno: %d, errmsg: %s", errno, strerror(errno));
      exit(LISTEN_ERROR);
    }
    // 监听成功，记录调试日志。
    LOG(DEBUG, "listen success, sockfd is : %d\n",
        _listensock); // 原有注释中的 \n 保留
  }

  /**
   * @brief 为单个客户端连接提供服务。
   *
   * 此方法实现了与一个已连接客户端的通信逻辑。在本例中，它是一个简单的回显服务：
   * 读取客户端发送的数据，然后将数据加上前缀发送回客户端。
   * 循环处理，直到客户端关闭连接或发生错误。
   * @param sockfd 与客户端通信的已连接套接字文件描述符。
   * @param client 客户端的网络地址信息。
   */
  void Service(int sockfd, InetAddr client) {
    // 记录一条日志，表明已建立新连接，并包含客户端的IP、端口和文件描述符。
    LOG(DEBUG, "get a new link, info %s:%d, fd : %d\n", client.Ip().c_str(),
        client.Port(), sockfd); // 原有注释中的 \n 保留

    // 构造一个包含客户端IP和端口的字符串，用于后续日志或输出的标识。
    std::string clientaddr =
        "[" + client.Ip() + ":" + std::to_string(client.Port()) + "]# ";

    // 循环处理来自客户端的请求。
    while (true) {
      char inbuffer[1024]; // 定义一个缓冲区来存储从客户端读取的数据。
      // 从客户端套接字读取数据。
      // read() 会阻塞，直到有数据可读、连接关闭或发生错误。
      // sizeof(inbuffer) - 1: 读取的最大字节数，留一个字节给字符串结束符 '\0'。
      ssize_t n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);

      if (n > 0) // 如果成功读取到 n 个字节的数据。
      {
        inbuffer[n] =
            0; // 在读取到的数据末尾添加空终止符，将其视为C风格字符串。
        // 将客户端地址和接收到的消息打印到服务器的标准输出。
        std::cout << clientaddr << inbuffer << std::endl;

        // 准备要回显给客户端的字符串。
        std::string echo_string = "[server echo]# "; // 添加服务器回显前缀。
        echo_string += inbuffer; // 将接收到的客户端消息追加到回显字符串。

        // 将回显字符串写回给客户端。
        // write()
        // 可能会发生部分写入，严格来说需要循环写入直到所有数据发送完毕。
        // 但对于简单的回显服务，通常假设一次能写完。
        write(sockfd, echo_string.c_str(), echo_string.size());
      }
      else if (n == 0) // 如果 read() 返回 0，表示客户端已正常关闭连接。
      {
        // client 退出&&关闭连接了
        // 记录客户端退出的信息。
        LOG(INFO, "%s quit\n", clientaddr.c_str()); // 原有注释中的 \n 保留
        break; // 跳出 while 循环，结束此客户端的服务。
      }
      else   // 如果 read() 返回负值，表示读取过程中发生错误。
      {
        // 记录读取错误，并包含客户端地址和错误号。
        LOG(ERROR, "read error from %s, errno: %d, errmsg: %s\n",
            clientaddr.c_str(), errno,
            strerror(errno)); // 原有注释中的 \n 保留，并添加了错误信息
        break;                // 跳出 while 循环，结束此客户端的服务。
      }
      // 原始代码中的 sleep(5); break; 会导致每个连接只处理一次请求。
      // 在一个典型的服务循环中，通常不会有这样的立即 break，除非是特定逻辑。
      // 为了演示，这里的 sleep 和 break
      // 仅为原始代码的呈现，实际服务器会持续通信。
      sleep(5); // 延时5秒 - 注意这会阻塞当前服务线程。
      break;    // 立即退出 while 循环 - 使得 Service 仅处理一次客户端的读写。
    } // while(true) 结束

    // 服务结束后的清理工作。
    // 原始代码中包含一些 shutdown 和 sleep
    // 的逻辑，可能用于测试或特定的关闭序列。
    // 在一个标准的服务函数结束后，应该关闭与该客户端通信的套接字。
    std::cout << "server开始退出"
              << std::endl; // 指示当前这个客户端连接的服务例程准备退出。
    // shutdown 可以用来优雅地关闭连接的一部分（读或写或两者）。
    // SHUT_RD 表示关闭连接的读方向，服务器将不能再从此套接字读取数据。
    // 客户端尝试写入数据时，服务器TCP栈会响应RST（如果套接字未关闭）。
    shutdown(sockfd, SHUT_RD);
    std::cout << "shut _ rd " << std::endl; // 确认读方向已关闭。
    sleep(10); // 等待一段时间，可能用于观察或测试某些行为。
               // shutdown(sockfd, SHUT_WR); //
               // 关闭连接的写方向，服务器将不能再写数据。
    //  客户端读取时会收到EOF。
    // std::cout << "shut _ wr " << std::endl;

    // 重要: 释放与此客户端连接相关的套接字资源。
    // 原始代码中注释了 ::close(sockfd); 并标注了“文件描述符泄漏”。
    // 如果不关闭，当大量客户端连接并断开后，服务器会耗尽文件描述符。
    // 即使使用了 shutdown，最终仍需 close 来彻底释放描述符。
    // 在当前多线程模型下，这个 close 应该在 HandlerSock 中，
    // 或者 Service 函数保证在返回前关闭。这里假设 Service 负责关闭。
    // 但由于 HandlerSock delete td; 而 Service 是 td->self->Service(...)，
    // sockfd 是 td 的成员，因此 Service 内部关闭 sockfd 是合理的。
    ::close(sockfd); // 关闭已连接的客户端套接字，释放资源。
    LOG(INFO, "Connection with %s (fd: %d) closed.", clientaddr.c_str(),
        sockfd);
  }

  /**
   * @brief 线程入口函数 (静态成员函数)。
   *
   * 当使用 pthread_create 创建新线程时，此函数作为新线程的起始执行点。
   * 它负责：
   * 1. 分离线程，使其在结束时自动回收资源。
   * 2. 将传入的 void* 参数转换回 ThreadData* 类型。
   * 3. 调用 TcpServer 实例的 Service 方法来处理客户端连接。
   * 4. 释放动态分配的 ThreadData 对象。
   * @param args 一个 void* 指针，实际指向一个 ThreadData
   * 对象，包含了处理连接所需的信息。
   * @return void* 通常返回 nullptr，因为线程已被分离。
   */
  static void *HandlerSock(void *args) {
    // 将当前线程设置为分离状态。
    // 分离的线程在终止时会自动释放其资源，主线程不需要 (也不能) 对其调用
    // pthread_join。
    pthread_detach(pthread_self());

    // 将传入的 void* 类型的参数强制类型转换为 ThreadData*。
    ThreadData *td = static_cast<ThreadData *>(args);

    // 调用 TcpServer 实例 (td->self) 的 Service 方法，
    // 传入客户端套接字 (td->sockfd) 和客户端地址 (td->clientaddr) 进行处理。
    td->self->Service(td->sockfd, td->clientaddr);

    // ThreadData 对象是在主线程的 Loop 方法中通过 new 动态分配的。
    // 当 Service 方法执行完毕，此线程的任务完成，需要释放这个 ThreadData
    // 对象以避免内存泄漏。
    delete td;
    td = nullptr; // 将指针置空，好习惯。

    // 线程函数返回 nullptr。
    return nullptr;
  }

  /**
   * @brief 服务器的主事件循环。
   *
   * 该方法启动服务器的监听和连接接受过程。它在一个循环中：
   * 1. 调用 accept() 等待并接受新的客户端连接。
   * 2. 对于每个成功接受的连接，根据所选择的并发模型（注释中列出了多种版本）
   * 创建一个新的执行单元（如新线程）来处理该连接。
   * 循环会持续到 _isrunning 标志变为 false。
   */
  void Loop() {
    _isrunning = true; // 将服务器的运行状态标志设置为 true。

    // 4. 不能直接接受数据，先获取连接
    // (这个注释来自原始代码，点明了服务器的工作流程)
    // 进入主循环，持续等待并接受客户端连接。
    while (_isrunning) {
      struct sockaddr_in peer; // 用于存储接受到的客户端的地址信息。
      socklen_t len = sizeof(
          peer); // 用于 accept 函数，传入地址结构的大小，并接收实际填充的大小。

      // 调用 ::accept() 阻塞等待客户端的连接请求。
      // _listensock: 服务器的监听套接字。
      // (struct sockaddr *)&peer: 指向存储客户端地址的结构体。
      // &len: 指向存储地址结构大小的变量。
      // 返回值 sockfd: 一个新的套接字文件描述符，用于与这个特定客户端通信。
      //              如果 accept 失败，返回 -1。
      int sockfd = ::accept(_listensock, (struct sockaddr *)&peer, &len);

      if (sockfd < 0) // 如果 accept() 调用失败。
      {
        // 记录警告日志。accept 失败可能是由多种原因造成的（例如，被信号中断
        // EINTR）。 通常，如果是可恢复的错误，服务器会继续尝试接受下一个连接。
        LOG(WARNING, "accept error, errno: %d, errmsg: %s\n", errno,
            strerror(errno)); // 原有注释中的 \n 保留，并添加错误信息
        continue;             // 继续下一次循环，尝试接受其他连接。
      }

      // 连接成功，sockfd 是与客户端通信的套接字。
      // peer 结构体中填充了客户端的地址信息。

      // Version 0 : 一次只能处理一个请求 --- 不可能
      // 这是最简单的单线程阻塞模型，服务器一次只能为一个客户端服务，效率极低。
      // Service(sockfd, InetAddr(peer));

      // Version 1: 采用多进程 (注释掉的代码块)
      // 每接受一个连接，就 fork() 一个子进程来处理。
      // pid_t id = fork();
      // if (id == 0) // 子进程
      // {
      //     // child : 关心sockfd， 不关心listensock
      //     ::close(_listensock); //
      //     建议子进程关闭监听套接字，因为它不需要监听。
      //     // if(fork() > 0) exit(0); //
      //     二次fork，使孙子进程成为孤儿，由init接管，避免僵尸。
      //     Service(sockfd, InetAddr(peer)); // (孙子)子进程处理服务。
      //     exit(0); // (孙子)子进程退出。
      // }
      // // father： 关心listensock，不关心sockfd
      // ::close(sockfd); //
      // 父进程关闭与客户端通信的套接字，因为它已交给子进程。 waitpid(id,
      // nullptr, 0); //
      // 父进程等待子进程结束，回收资源，避免僵尸。这会阻塞父进程。

      // version 2: 采用多线程 (当前代码实际使用的模型)
      // 每接受一个连接，就创建一个新线程来处理。
      pthread_t t; // 用于存储新线程的ID。
      // 动态分配一个 ThreadData 对象，用于向新线程传递参数。
      // this 指针作为参数传递，以便新线程能够调用当前 TcpServer 实例的 Service
      // 方法。
      ThreadData *td = new ThreadData(sockfd, InetAddr(peer), this);
      // 创建一个新线程。
      // &t: 存储新线程ID。
      // nullptr: 使用默认线程属性。
      // HandlerSock: 线程的入口函数 (必须是静态成员函数或全局函数)。
      // td: 传递给 HandlerSock 的参数。
      pthread_create(&t, nullptr, HandlerSock,
                     td); // 将线程分离 (原始注释，实际分离在HandlerSock内)
                          // 注意：HandlerSock 内部调用了
      // pthread_detach(pthread_self())，所以这里不需要再 detach。 如果不在
      // HandlerSock 内部 detach，可以在这里调用 pthread_detach(t) 或稍后
      // pthread_join(t, nullptr)。

      // vesion 3: 采用线程池 (注释掉的代码块)
      // 将处理连接的任务提交给预先创建好的线程池。
      // std::bind 用于将成员函数 Service 与其对象实例 this 以及参数 sockfd,
      // InetAddr(peer) 绑定， 形成一个符合 task_t (即 std::function<void()>)
      // 类型的可调用对象。 task_t t = std::bind(&TcpServer::Service, this,
      // sockfd, InetAddr(peer)); 获取线程池的单例，并将任务加入队列等待执行。
      // ThreadPool<task_t>::GetInstance()->Enqueue(t);
      // 这种方式可以复用线程，避免频繁创建和销毁线程的开销。
    }
    _isrunning =
        false; // 当主循环退出时（例如，通过外部信号或其他机制使 _isrunning 变为
               // false）， 将服务器运行状态明确设置为 false。
  }

  /**
   * @brief TcpServer 类的析构函数。
   *
   * 负责在 TcpServer 对象销毁时释放占用的资源，主要是关闭监听套接字。
   */
  ~TcpServer() {
    // 检查监听套接字 _listensock 是否是一个有效的文件描述符
    // （即不是初始的 defaultsockfd 值 -1，并且可能已经被成功创建）。
    if (_listensock > defaultsockfd) // 更准确的检查是 _listensock !=
                                     // defaultsockfd 且 _listensock >= 0
      ::close(_listensock);          // 关闭监听套接字，释放相关系统资源。
  }

private:
  uint16_t _port;  // 存储服务器配置的监听端口号。
  int _listensock; // 存储监听套接字的文件描述符。
  bool _isrunning; // 标志服务器主循环是否应该继续运行。
};